package ru.gelin.android.bells.timers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *  Collections of timers.
 *  Each timer in the collection is identified by the ID.
 *  ID is assigned to the timer during insertion.
 *  ID is used to find timer in the collection during update.
 */
public class TimerCollection {

    /** Map of timers by ID */
    Map<Integer, Timer> timers = new HashMap<Integer, Timer>();
    
    /** IDs counter */
    int idCounter;
    
    /**
     *  Inserts new timer to collection.
     *  Sets the ID for the timer.
     *  @param  timer   ID of this timer is set
     */
    public void insert(Timer timer) {
        int id = getNextId();
        timer.setId(id);
        timers.put(id, timer.copy());
    }
    
    /**
     *  Updates timer in the collection.
     *  The timer with specified ID is updated
     *  @param  timer   timer (ID and values) to update
     */
    public void update(Timer timer) {
        timers.put(timer.getId(), timer.copy());
    }

    /**
     *  Selects one timer by ID.
     */
    public Timer select(int id) {
        return timers.get(id).copy();
    }
    
    /**
     *  Deletes one timer by ID.
     */
    public Timer delete(int id) {
        return timers.remove(id);
    }
    
    /**
     *  Deletes all timers from the collection.
     */
    public void delete() {
        timers.clear();
    }

    /**
     *  Returns the list of timers in the collection.
     *  The timers are ordered by the time of the alarm time acceding.
     *  Not the actual, but the deep copy of the original collection
     *  is returned, to modify any element of the collection perform
     *  {@link #update}.
     *  @param  now current time, is used to sort the list
     */
    public List<Timer> select(Date now) {
        List<Timer> result = new ArrayList<Timer>(timers.size());
        for (Timer timer : internalList(now)) {
            result.add(timer.copy());
        }
        return result;
    }
    
    /**
     *  Returns sorted list of actual timers saved in the collection.
     *  @param  now current time, is used to sort the list
     */
    protected List<Timer> internalList(Date now) {
        List<Timer> result = new ArrayList<Timer>(timers.size());
        result.addAll(timers.values());
        Collections.sort(result, new TimerComparator(now));
        return result;
    }
    
    /**
     *  Returns unsorted list of actual timers saved in the collection.
     */
    protected List<Timer> internalList() {
        List<Timer> result = new ArrayList<Timer>(timers.size());
        result.addAll(timers.values());
        return result;
    }

    /**
     *  Returns the current size of the collection.
     */
    public int size() {
        return timers.size();
    }
    
    /**
     *  Selects all timers which alarms have came.
     *  <pre>
     *              now
     *  -------------|---------------
     *     start        alarm
     *  ----------------|------------
     *     start                alarm
     *     ----period----
     *  </pre>
     */
    public List<Timer> selectAlarms(Date now) {
        Date nowMinute = Timer.normalizeDate(now);
        List<Timer> result = new ArrayList<Timer>();
        for (Timer timer : internalList(now)) {
            if (!timer.isEnabled()) {
                continue;
            }
            Date start = timer.getStartTime();
            if (!start.before(nowMinute)) {
                continue;   //will start in future
            }
            Date startPlusPeriod = timer.getPeriod().add(timer.getStartTime());
            if (!startPlusPeriod.after(nowMinute)) {
                result.add(timer.copy());
            } else {
                break;
            }
        }
        return result;
    }

    /**
     *  Normalizes all alarms. I.e. shifts all it's times to make alarm time
     *  in future.
     *  Note that after calling this method {@link #selectAlarms} will
     *  return empty list. 
     */
    public void normalize(Date now) {
        for (Timer timer : internalList(now)) {
            timer.normalize(now);
        }
    }
    
    /**
     *  Returns nearest alarm time, i.e. the alarm time of the first
     *  enabled timer in the sorted list.
     *  @return next alarm date or <code>null</code> if there is no  enabled timers
     */
    public Date alarmTime(Date now) {
        for (Timer timer : internalList(now)) {
            if (timer.isEnabled()) {
                return timer.getAlarmTime(now);
            }
        }
        return null;
    }

    /**
     *  Returns next ID for the inserting Timer
     */
    int getNextId() {
        return ++idCounter;
    }

}
